
package com.borqs.music.executor;

import static com.borqs.music.executor.Task.Status.CANCELED;
import static com.borqs.music.executor.Task.Status.DONE;
import static com.borqs.music.executor.Task.Status.FAILED;
import static com.borqs.music.executor.Task.Status.NONE;
import static com.borqs.music.executor.Task.Status.RUNNING;

import org.json.JSONObject;

import android.util.Log;

import com.borqs.music.http.HttpConfiguration;
import com.borqs.music.http.HttpConfigurationImpl;
import com.borqs.music.http.HttpRequest;
import com.borqs.music.http.HttpResponse;
import com.borqs.music.http.MusicHttpClientWrapper;

/**
 * Task is a time-consuming job.<br></br>
 * Sub class must implement the abstract method 
 * {@link #prepareRequest()} and {@link #handleResponse(HttpResponse)}.<br></br>
 * you should use Executor.submit() to add a task into the queue.
 */
public abstract class Task implements Runnable {

    protected TaskListener mListener;

    protected Status status = NONE;

    protected Object mResult;

    private HttpRequest mRequest;
    private HttpResponse mResponse;
    private static final TaskListener NULL_LISTENER = new TaskAdapter();
    
    public Task() {
        this(NULL_LISTENER);
    }

    public Task(TaskListener listener) {
        if (listener != null) {
            mListener = listener;
        } else {
            mListener = NULL_LISTENER;
        }
    }

    public final TaskListener getListener() {
        return mListener;
    }
    
    public final void setListener(TaskListener listener) {
        if (listener != null) {
            mListener = listener;
        } else {
            mListener = NULL_LISTENER;
        }
    }


    /**
     * Sub-class must implement this method. 
     * Use the method to prepare your Request
     */
    public abstract HttpRequest prepareRequest();

    /**
     * Sub-class must implement this method. 
     * Use the method to handle the response.
     */
    public abstract Object handleResponse(HttpResponse resp) throws Exception;

    /**
     * implement Runnable
     */
    @Override
    public final void run() {
        if (isCanceled()) {
            mListener.onCancel(this);
            return;
        }
        
        try {
            status = RUNNING;
            mListener.onStart(this);
            mRequest = prepareRequest();

            if (isCanceled()) {
                mListener.onCancel(this);
                return;
            }

            MusicHttpClientWrapper client = MusicHttpClientWrapper.getInstance(getConfig());
            mResponse = client.request(mRequest);
            if (isCanceled()) {
                mListener.onCancel(this);
                return;
            }

            mResult = handleResponse(mResponse);

            if (isCanceled()) {
                mListener.onCancel(this);
                return;
            } else {
                status = DONE;
                mListener.onDone(this);
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (isCanceled()) {
                mListener.onCancel(this);
            } else {
                status = FAILED;
                mListener.onFail(this);
            }
        }
    }
    
    public final Status getStatus(){
        return status;
    }

    public final boolean isCanceled() {
        return (status == CANCELED);
    }

    public final boolean cancel() {
        if (status == RUNNING || status == NONE) {
            status = CANCELED;
            return true;
        }

        return false;
    }

    public final boolean isRunning() {
        return (status == RUNNING);
    }
    
    public final boolean isFailed() {
        return (status == FAILED);
    }
    
    public final boolean isDone() {
        return (status == DONE);
    }

    /** 
     * Get the ask result, which is return by {@link #handleResponse(HttpResponse)}
     * @return
     */
    public final Object getResult() {
        return mResult;
    }

    /**
     * Use the method override the HttpConfiguration
     * @return your own configuration
     */
    public HttpConfiguration getConfig() {
        HttpConfiguration conf = new HttpConfigurationImpl();
        return conf;
    }
    
    /**
     * Task status
     */
    public static enum Status {
        NONE, RUNNING, CANCELED, FAILED, DONE
    }
    
    private ErrorReason errorReason;
    
    public ErrorReason getErrorReason(){
        if(null == errorReason){
            errorReason = new ErrorReason();
            errorReason.code = -1000;
            errorReason.message = "Unknow error";
        }
        return errorReason;
    }
    
    public void checkResponse(HttpResponse resp) throws Exception {
        JSONObject jobj = null;
        try {
            jobj = resp.asJSONObject();
        } catch (Exception ex) {
            // Ignore the exception
        }

        if (jobj != null && jobj.has("error_code")) {
            errorReason = new ErrorReason();
            errorReason.code = jobj.getInt("error_code");
            errorReason.message = jobj.getString("error_msg");
            throw new Exception("Server Report Error: " + resp.asString());
        }
    }
}
